package com.neuronbit.businessflow.intellij.psi;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ProcessingContext;
import com.neuronbit.businessflow.intellij.antlr.BpmParser;
import com.neuronbit.businessflow.intellij.psi.reference.MethodCallReference;
import com.neuronbit.businessflow.intellij.psi.reference.SimpleReference;
import org.antlr.intellij.adaptor.lexer.RuleIElementType;
import org.antlr.intellij.adaptor.psi.ANTLRPsiNode;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

import static com.neuronbit.businessflow.intellij.antlr.BpmParser.*;


/**
 * The type Simple reference contributor.
 */
public final class SimpleReferenceContributor extends PsiReferenceContributor {

    @Override
    public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
        PsiReferenceProvider provider = new PsiReferenceProvider() {
            @Override
            public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
                IElementType elementType = element.getNavigationElement().getNode().getElementType();
                if (elementType instanceof RuleIElementType && ((RuleIElementType) elementType).getRuleIndex() == BpmParser.RULE_callDef) {
                    List<PsiReference> references = new ArrayList<>();
                    String methodName = "";
                    List<PsiElement> params = new ArrayList<>();
                    PsiClass psiClass = null;
                    for (PsiElement child : element.getChildren()) {
                        IElementType childElementType = child.getNode().getElementType();
                        if (childElementType instanceof RuleIElementType) {
                            int ruleIndex = ((RuleIElementType) childElementType).getRuleIndex();

                            if (ruleIndex == RULE_attribute && "method".equalsIgnoreCase(child.getFirstChild().getText())) {
                                methodName = StringUtils.strip(child.getLastChild().getText(), "\"");
                            }

                            if (ruleIndex == RULE_paramArray) {
                                for (PsiElement paramArrayChild : child.getChildren()) {
                                    IElementType paramArrayChildType = paramArrayChild.getNode().getElementType();
                                    if (paramArrayChildType instanceof RuleIElementType && ((RuleIElementType) paramArrayChildType).getRuleIndex() == RULE_param) {
                                        params.add(paramArrayChild);
                                    }
                                }
                            }

                            if (ruleIndex == RULE_attribute && "type".equalsIgnoreCase(child.getFirstChild().getText())) {
                                String className = StringUtils.strip(child.getLastChild().getText(), "\"");
                                if (StringUtils.isNotBlank(className)) {
                                    Project project = child.getProject();
                                    psiClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));

                                    String text = element.getText();
                                    //TODO shunyun 2024/3/27: 可以在语法解析器里面做这个拆分的事情
                                    String simpleClassName = StringUtils.substringAfterLast(className, ".");
                                    String packageName = StringUtils.substringBeforeLast(className, ".");
                                    int packageCount = StringUtils.countMatches(packageName, ".") + 1;

                                    for (int i = 0; i < packageCount; i++) {
                                        PsiPackage psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName);
                                        if (psiPackage != null) {
                                            String simplePackageName = StringUtils.contains(packageName, ".") ? StringUtils.substringAfterLast(packageName, ".") : packageName;
                                            int startOffset = StringUtils.indexOf(text, simplePackageName, 48);
                                            references.add(new SimpleReference(element, psiPackage, new TextRange(startOffset, startOffset + simplePackageName.length())));
                                        }
                                        packageName = StringUtils.substringBeforeLast(packageName, ".");
                                    }

                                    if (null != psiClass) {
                                        int startOffset = StringUtils.indexOf(text, simpleClassName);
                                        references.add(new SimpleReference(element, psiClass, new TextRange(startOffset, startOffset + simpleClassName.length())));
                                    }
                                }
                            }
                        }
                    }

                    if (null != psiClass && StringUtils.isNotBlank(methodName)) {
                        String elementText = element.getText();
                        PsiMethod[] methodsByName = psiClass.findMethodsByName(methodName, true);
                        for (PsiMethod psiMethod : methodsByName) {
                            @NotNull PsiParameterList parameters = psiMethod.getParameterList();
                            if (parameters.getParametersCount() != params.size()) {
                                continue;
                            }
                            boolean match = true;
//                            for (int i = 0; i < parameters.getParametersCount(); i++) {
//                                PsiParameter parameter = parameters.getParameter(i);
//                                //TODO shunyun 2024/4/2: 类型属性已经移除，这里的比对逻辑需要修改
//                                if (parameter == null || !parameter.getType().getCanonicalText().equals(getAttributeValue("type", params.get(i)))) {
//                                    match = false;
//                                    break;
//                                }
//                            }
                            if (match) {
                                //add method name ref
                                int startOffset = findStartOffset(elementText, methodName, "method=\"");
                                references.add(new MethodCallReference(element, psiMethod, new TextRange(startOffset, startOffset + methodName.length())));
                                //add method params ref
//                                for (int i = 0; i < params.size(); i++) {
//                                    String paramName = getAttributeValue("as", params.get(i));
//                                    PsiParameter parameter = parameters.getParameter(i);
//                                    if (null == paramName || parameter == null) {
//                                        continue;
//                                    }
//                                    int offset = findStartOffset(elementText, paramName, "as=\"");
//                                    references.add(new SimpleReference(element, parameter, new TextRange(offset, offset + paramName.length())));
//                                }
                            }
                        }
                    }
                    return references.toArray(new PsiReference[0]);
                }
                return PsiReference.EMPTY_ARRAY;
            }

            private static int findStartOffset(String text, String methodName, String prefix) {
                return StringUtils.indexOf(text, prefix + methodName) + prefix.length();
            }

            private String getAttributeValue(String attributeName, PsiElement element) {
                IElementType elementType = element.getNode().getElementType();
                if (elementType instanceof RuleIElementType) {
                    int ruleIndex = ((RuleIElementType) elementType).getRuleIndex();
                    if (ruleIndex == RULE_attribute && attributeName.equalsIgnoreCase(element.getFirstChild().getText())) {
                        return StringUtils.strip(element.getLastChild().getText(), "\"");
                    } else {
                        for (PsiElement child : element.getChildren()) {
                            String attributeValue = getAttributeValue(attributeName, child);
                            if (null != attributeValue) {
                                return attributeValue;
                            }
                        }
                    }
                }
                return null;
            }

        };
        PsiElementPattern.Capture<ANTLRPsiNode> pattern = PlatformPatterns.psiElement(ANTLRPsiNode.class);
        registrar.registerReferenceProvider(pattern, provider);

    }


}
